﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;

/// <summary>
/// A representation of an item from the models list
/// </summary>
public class ModelItem
{ 
    public List<string> m_textdata = new List<string>();    
  
    public string m_name;    
    public string m_path;
    public string m_thumbnailPath;
    
    public List<string> m_shaders = new List<string>();    
    public List<Texture> m_textures = new List<Texture>();   

    public int m_nameID = -1;
    public int m_pathID = -1;
    public int m_thumbnailPathID = -1;
    public List<int> m_shadersIDs = new List<int>();
    public List<int> m_texturesIDs = new List<int>();    

    public void EditName(string a_name)
    {
        if (m_name == "" || m_name == null)
        {
            m_nameID = m_textdata.Count;
            AddLine(Strings.ModelItem.ListCommands.name + " " + a_name);
            
        }
        else
        {
            UpdateLine(Strings.ModelItem.ListCommands.name + " " + a_name, m_nameID);
        }

        m_name = a_name;        
    }

    public void EditPath(string a_path)
    {
        if (m_path == "" || m_path == null)
        {
            m_pathID = m_textdata.Count;
            AddLine(Strings.ModelItem.ListCommands.path + " " + a_path);

        }
        else
        {
            UpdateLine(Strings.ModelItem.ListCommands.path + " " + a_path, m_pathID);
        }

        m_path = a_path;        
    }

    public void EditThumbnailPath(string a_path)
    {
        if (m_thumbnailPath == "" || m_thumbnailPath == null)
        {
            m_thumbnailPathID = m_textdata.Count;
            AddLine(Strings.ModelItem.ListCommands.thumbnail + " " + a_path);

        }
        else
        {
            UpdateLine(Strings.ModelItem.ListCommands.thumbnail + " " + a_path, m_thumbnailPathID);
        }
        
        m_thumbnailPath = a_path;        
    }

    /// <summary>
    /// Takes a list of shaders
    /// </summary>    
    public void EditShaders(List<string> a_shaders)
    {
        //Note: It's more optimized to flip the for loops but reason for not is that it's easier this way with getting the nextID        

        int f_nextID = (m_shadersIDs.Count > 0) ? f_nextID = m_shadersIDs[m_shadersIDs.Count - 1] + 1 : m_textdata.Count;

        //Loop through to see what shaders already existed
        foreach (string shader in a_shaders)
        {
            bool f_found = false;
            for (int i = 0; i < m_shaders.Count; i++)
            {
                if (shader == m_shaders[i])
                {
                    f_found = true;
                    break;
                }
            }

            if (!f_found)
            {
                AddLine(Strings.ModelItem.ListCommands.shader + " " + shader, f_nextID);
                m_shaders.Add(shader);
                m_shadersIDs.Add(f_nextID++);
            }            
        }

        //Loop through the existing shaders and see if one exists that is not part of a_shaders list
        for (int i = 0; i < m_shaders.Count; )
        {
            bool f_found = false;
            foreach (string shader in a_shaders)
            {
                if (m_shaders[i] == shader)
                {
                    f_found = true;
                    break;
                }
            }

            if (f_found)
            {
                i++;                
            }
            else
            {
                RemoveLine(m_shadersIDs[i]);
            }
        }       
    }

    /// <summary>
    /// Edits a texture based on type and path. Deletes the texture if path is empty. Adds it to m_textData if it doesn't already exist. If it exists it updates the current path
    /// </summary>    
    public void EditTexture(Texture.Type a_type, string a_path)
    {
        //NOTE: No need to be careful about validation since Validate() function should handle that. Plus it was validated in the TextureSelection control.
        if (a_path == "" || a_path == null)
        {
            RemoveTexture(a_type);
        }
        else
        {
            bool f_existed = false;
            for( int i = 0; i < m_textures.Count; i++)
            {
                if (m_textures[i].m_type == a_type)
                {
                    m_textures[i].m_path = a_path;
                    UpdateLine(Texture.GetCommandTypeString(a_type) + " " + a_path, m_texturesIDs[i] );
                    f_existed = true;
                    break;
                }
            }

            if (!f_existed)
            {
                Texture f_texture = new Texture(a_path, a_type);
                int f_nextID = (m_texturesIDs.Count > 0) ? m_texturesIDs[m_texturesIDs.Count - 1] + 1 : m_textdata.Count;

                AddLine(Texture.GetCommandTypeString(a_type) + " " + f_texture.m_path, f_nextID);
                m_textures.Add(f_texture);
                m_texturesIDs.Add(f_nextID);
            }
        }
    }    

    private void RemoveTexture(Texture.Type a_type)
    {
        for (int i = 0; i < m_textures.Count; i++)
        {
            if (m_textures[0].m_type == a_type)
            {
                RemoveLine(m_texturesIDs[i] );
            }
            else
            {
                i++;
            }
        }
    }

    /// <summary>
    /// Adds a line to the item
    /// </summary>    
    /// <param name="a_id">the id where line is inserted</param>
    public void AddLine(string a_line, int a_id = -1)
    {        
        if (a_id < 0 || a_id > m_textdata.Count)
        {
            a_id = m_textdata.Count;
        }

        if (a_id != m_textdata.Count)
        {
            //Check if their ID is bigger than where the line is inserted, if it is update their new index
            if (m_nameID >= a_id)
            {
                m_nameID++;
            }
            if (m_pathID >= a_id)
            {
                m_pathID++;
            }
            if (m_thumbnailPathID >= a_id)
            {
                m_thumbnailPathID++;
            }
            for (int i = 0; i < m_shadersIDs.Count; i++)
            {
                if (m_shadersIDs[i] >= a_id)
                {
                    m_shadersIDs[i]++;
                }
            }
            for (int i = 0; i < m_texturesIDs.Count; i++)
            {
                if (m_texturesIDs[i] >= a_id)
                {
                    m_texturesIDs[i]++;
                }
            }

            m_textdata.Insert(a_id, a_line);
        }
        //If no id was given add it at the end
        else
        {
            m_textdata.Add(a_line);
        }        
    }

    /// <summary>
    /// Removes a textline from the textdata array
    /// </summary>
    /// <param name="a_id"></param>
    public void RemoveLine(int a_id)
    {
        if (a_id < 0 || a_id > m_textdata.Count)
        {            
            return;
        }
        //Check if their ID is bigger than where the line is inserted, if it is update their new index
        if (m_nameID > a_id)
        {
            m_nameID--;
        }
        if (m_pathID > a_id)
        {
            m_pathID--;
        }
        if (m_thumbnailPathID > a_id)
        {
            m_thumbnailPathID--;
        }

        for (int i = 0; i < m_shadersIDs.Count;)
        {
            if (m_shadersIDs[i] > a_id)
            {
                m_shadersIDs[i]--;
            }
            //If the ids are the same then remove it from the shaders list aswell
            else if (m_shadersIDs[i] == a_id)
            {
                m_shadersIDs.RemoveAt(i);
                m_shaders.RemoveAt(i);
                continue;
            }

            i++;
        }
        for (int i = 0; i < m_texturesIDs.Count;)
        {
            if (m_texturesIDs[i] > a_id)
            {
                m_texturesIDs[i]--;
            }
            //If the ids are the same then remove it from the textures list aswell
            else if (m_texturesIDs[i] == a_id)
            {
                m_texturesIDs.RemoveAt(i);
                m_textures.RemoveAt(i);
                continue;
            }

            i++;
        }

        m_textdata.RemoveAt(a_id);
    }

    /// <summary>
    /// Updates a string in m_textdata
    /// </summary>
    /// <param name="a_value">The new value</param>
    /// <param name="a_id">The id to update</param>
    public void UpdateLine(string a_value,int a_id)
    {
        if (a_id < 0 || a_id > m_textdata.Count)
        {
            return;
        }

        m_textdata[a_id] = a_value;
    }

    /// <summary>
    /// This deletes the first occurence element of name, path & thumbnail path.
    /// Deletes the later entries of shader & texture
    /// </summary>
    public void DeleteDuplicates()
    {
        //NOTE: Reason for removing it in that order is because of the order it's read. 
        //Texture read from first value. Shader I am not sure actually!

        int f_nameID = -1,
            f_pathID = -1,
            f_thumbPathID = -1;

        bool f_phongShader = false, f_wireframeShader = false,
            f_normalTex = false, f_diffuseTex = false;

        for (int i = 0; i < m_textdata.Count; )
        {
            string f_command = m_textdata[i];
            bool f_removed = false;

            if (f_command.StartsWith(Strings.ModelItem.ListCommands.name))
            {
                if (f_nameID != -1)
                {
                    RemoveLine(f_nameID);
                    f_removed = true;
                }

                f_nameID = i;
            }
            else if (f_command.StartsWith(Strings.ModelItem.ListCommands.path))
            {
                if (f_pathID != -1)
                {
                    RemoveLine(f_pathID);
                    f_removed = true;
                }

                f_pathID = i;
            }
            else if (f_command.StartsWith(Strings.ModelItem.ListCommands.thumbnail))
            {
                if (f_thumbPathID != -1)
                {
                    RemoveLine(f_thumbPathID);
                    f_removed = true;
                }

                f_thumbPathID = i;
            }
            else if (f_command.StartsWith(Strings.ModelItem.ListCommands.shader))
            {
                string f_shader = f_command.Split(Strings.ModelItem.ModelItem_LineDataSplitter)[1];

                if (String.Equals(f_shader, Strings.ModelItem.ShaderTypes.phong, StringComparison.OrdinalIgnoreCase  ) )
                {
                    if (!f_phongShader)
                    {
                        f_phongShader = true;
                    }
                    else
                    {
                        RemoveLine(i);
                        f_removed = true;
                    }
                }
                else if (String.Equals(f_shader, Strings.ModelItem.ShaderTypes.wireframe, StringComparison.OrdinalIgnoreCase))
                {
                    if (!f_wireframeShader)
                    {
                        f_wireframeShader = true;
                    }
                    else
                    {
                        RemoveLine(i);
                        f_removed = true;
                    }
                }
            }
            else if (f_command.StartsWith(Strings.ModelItem.ListCommands.diffusemap))
            {
                if (!f_diffuseTex)
                {
                    f_diffuseTex = true;
                }
                else
                {
                    RemoveLine(i);
                    f_removed = true;
                }
            }
            else if (f_command.StartsWith(Strings.ModelItem.ListCommands.normalmap))
            {
                if (!f_normalTex)
                {
                    f_normalTex = true;
                }
                else
                {
                    RemoveLine(i);
                    f_removed = true;
                }
            }

            if (!f_removed)
            {
                i++;
            }
        }
    }

    public Texture GetTexture(Texture.Type a_type)
    {
        for (int i = 0; i < m_textures.Count; i++)
        {
            if (m_textures[i].m_type == a_type)
            {
                return m_textures[i];
            }
        }

        return null;
    }

    /// <summary>
    /// Returns the string of an item to be written to models.txt
    /// </summary>    
    public override string ToString()
    {
        System.Text.StringBuilder f_result = new System.Text.StringBuilder();

        for (int i = 0; i < m_textdata.Count; i++)
        {
            f_result.Append(m_textdata[i] + Environment.NewLine );
        }

        return f_result.ToString();           
    }

    public ModelItem Clone()
    {
        ModelItem f_item = new ModelItem();
        f_item.Copy(this);
        
        return f_item;
    }

    /// <summary>
    /// Copies values from a_item to this
    /// </summary>
    public void Copy(ModelItem a_item)
    {
        m_name = a_item.m_name;
        m_nameID = a_item.m_nameID;
        m_path = a_item.m_path;
        m_pathID = a_item.m_pathID;

        m_thumbnailPath = a_item.m_thumbnailPath;
        m_thumbnailPathID = a_item.m_thumbnailPathID;

        m_shaders.Clear();
        m_shadersIDs.Clear();
        for (int i = 0; i < a_item.m_shaders.Count; i++)
        {
            m_shaders.Add(a_item.m_shaders[i]);
            m_shadersIDs.Add(a_item.m_shadersIDs[i]);
        }

        m_textures.Clear();
        m_texturesIDs.Clear();
        for (int i = 0; i < a_item.m_textures.Count; i++)
        {
            Texture f_texture = a_item.m_textures[i];
            m_textures.Add(new Texture(f_texture.m_path, f_texture.m_type));

            m_texturesIDs.Add( a_item.m_texturesIDs[i]);
        }

        m_textdata.Clear();
        for (int i = 0; i < a_item.m_textdata.Count; i++)
        {
            m_textdata.Add(a_item.m_textdata[i]);
        }
    }

    
}